package nats_cli

import (
	"encoding/json"
	"fmt"
	"gluenet/internal/glue-srvp/adapter/nats-cli/nats"
	"gluenet/internal/glue-srvp/domain/dependency"
	"gluenet/internal/glue-srvp/domain/entity"
	"gluenet/internal/pkg/infra"
	"gluenet/internal/pkg/infra/prepare"
	"gluenet/pkg/agent/option"
	"gluenet/pkg/agent/types"
	"strings"
	"time"
)

func init() {
	prepare.RegisterRepo(func() {
		cli := NewInstanceCLI()
		prepare.InjectDependency(func() dependency.InstanceCLI {
			return cli
		})
	})
}

type Instance struct {
}

func (i Instance) StartObject(GUID string, Runtime interface{}) error {
	//nats.CLI
	return nil
}

func (i Instance) StopObject(GUID string, Runtime interface{}) error {
	//TODO implement me
	panic("implement me")
}

func (i Instance) CreateObject(obj *entity.Instance) error {
	switch obj.Platform {
	case infra.PlatformKubernetes:
		var withJaeger, withLinkerd = false, false
		if obj.DetailRepoValue.OptionJaeger == infra.Enable {
			withJaeger = true
		}
		if obj.DetailRepoValue.OptionLinkerd == infra.Enable {
			withLinkerd = true
		}

		switch obj.Kind {
		case infra.ConfigKindHelmChart:
			runTime, err := nats.CLI.Agent(obj.RunTimeRepoValue.ClusterAgentGuid).Releases(obj.GUID).Create(&types.Release{
				ChartName:   strings.Join([]string{"gluenet.io", obj.Config.Name}, "/"),
				ReleaseName: obj.Name,
			}, option.CreateOptions{
				WithJaeger:  withJaeger,
				WithLinkerd: withLinkerd,
			})
			if err != nil {
				return err
			}
			obj.RunTimeRepoValue.ContainerCount = int64(len(runTime.Spec.Containers))
			//runtimeDetail.State = make(map[string]int, len(runTime.Spec.Containers))
			//runtimeDetail.Containers = make(map[string]po.InstanceContainer, len(runTime.Spec.Containers))
			//for _, v := range runTime.Spec.Containers {
			//	runtimeDetail.State[v] = infra.NotReady
			//	runtimeDetail.Containers[v] = po.InstanceContainer{}
			//}
			//
			//runtimeDetail.Spec = runTime.Spec
			//runtimeDetail.Platform = obj.Platform
		case infra.ConfigKindKubeYaml:
		}
	default:
		return fmt.Errorf("unknow platform")
	}
	return nil
}

//func getDetailValueContainers(detailValue interface{}) ([]interface{}, error) {
//	detail, ok := detailValue.(map[string]interface{})
//	if !ok {
//		return nil, fmt.Errorf("detail value not map[string]interface{}{}")
//	}
//	specs, ok := detail["spec"]
//	if !ok {
//		return nil, fmt.Errorf("detail value not contain spec")
//	}
//	spec, ok := specs.(map[string]interface{})
//	if !ok {
//		return nil, fmt.Errorf("detail value not map[string]interface{}{}")
//	}
//	containers, ok := spec["containers"]
//	if !ok {
//		return nil, fmt.Errorf("spec not contain containers")
//	}
//	cts, ok := containers.([]interface{})
//	if !ok {
//		return nil, fmt.Errorf("containers not map[string]interface{}{}")
//	}
//	return cts, nil
//}

//func (i Instance) RemoveObject(obj *entity.Instance) error {
//	run, ok := Runtime.(map[string]interface{})
//	if !ok {
//		return fmt.Errorf("runtime is not map[string]interface{}")
//	}
//	agents, ok := run["agents"]
//	if !ok {
//		return fmt.Errorf("runtime can't find agents")
//	}
//	platform, ok := run["platform"]
//	if !ok {
//		return fmt.Errorf("runtime can't find platform")
//	}
//	switch platform {
//	case infra.PlatformKubernetes:
//		err := nats.CLI.Agent(agents.(string)).Releases(GUID).Delete(option.DeleteOptions{})
//		if err != nil {
//			return fmt.Errorf("delete %v", err)
//		}
//		return nil
//	case infra.PlatformGluenets:
//		agent_guidi, ok := agents.(map[string]interface{})
//		if !ok {
//			return fmt.Errorf("agents is not map[string]interface{}")
//		}
//		for contain, agent := range agent_guidi {
//			err := nats.CLI.Agent(agent.(string)).Containers(strings.Join([]string{GUID, contain}, "_")).Delete(option.DeleteOptions{})
//			if err != nil {
//				return fmt.Errorf("delete %v", err)
//			}
//		}
//
//		return nil
//	}
//	return fmt.Errorf("unknow platform")
//}

func (i Instance) RemoveObject(obj *entity.Instance) error {
	switch obj.Platform {
	case infra.PlatformKubernetes:
		err := nats.CLI.Agent(obj.RunTimeRepoValue.ClusterAgentGuid).Releases(obj.GUID).Delete(option.DeleteOptions{})
		if err != nil {
			return fmt.Errorf("delete %v", err)
		}
		return nil
	}
	return fmt.Errorf("unknow platform")
}

func (i Instance) HttpProxy(agentGuid string, url string) (interface{}, error) {
	//fmt.Printf("url: %v", url)

	var req = types.ProxyGet{
		HttpUrl: url,
	}

	var wapper = struct {
		Spec interface{} `json:"spec,omitempty"`
	}{req}

	bts, _ := json.Marshal(wapper)
	resp, err := nats.CLI.RPCClient().Nats().IRequest(agentGuid+":"+infra.RPCHttpProxyGet, bts, time.Minute)
	if err != nil {
		return nil, err
	}

	//fmt.Printf("\n\n %s \n\n", resp)
	var proxyResp = types.HttpProxyResp{}
	err = json.Unmarshal(resp, &proxyResp)
	if err != nil {
		return nil, err
	}

	if proxyResp.IsSuccess {
		return fmt.Sprintf("%s", proxyResp.Data), nil
	}
	return nil, fmt.Errorf("req err %v", proxyResp.Message)
}

func (i Instance) InfoStrategy(strategy string, bestApp, guarantApp []string) (interface{}, error) {
	best := make(map[string]*entity.Instance, len(bestApp))
	guarant := make(map[string]*entity.Instance, len(guarantApp))

	for _, v := range bestApp {
		best[v] = &entity.Instance{}
	}

	for _, v := range guarantApp {
		guarant[v] = &entity.Instance{}
	}

	info := map[string]interface{}{
		"best_app":    best,
		"guarant_app": guarant,
	}
	bts, _ := json.Marshal(info)
	res, err := nats.CLI.RPCClient().Nats().IRequest(strategy+":instance.set", bts, time.Minute)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func NewInstanceCLI() *Instance {
	r := &Instance{}
	return r
}
